
//{{{ file documentation -------------------------------------------------------

/*!
 * @file     textedit.cpp
 * @brief    dicodor text edition widget class implementation
 */

//}}} --------------------------------------------------------------------------
//{{{ includes / predeclarations -----------------------------------------------

// @ (#) $Id:$

#include "textedit.h"

#include <QAction>
#include <QWidgetAction>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QMessageBox>
#include	 <QDebug>

//---------------------------------------------
/**
 * @brief SyntaxHighlighter::SyntaxHighlighter
 * @param parent
 */
SyntaxHighlighter::SyntaxHighlighter (QTextDocument *parent) :
    QSyntaxHighlighter (parent)
{
    m_alertFormat.setUnderlineColor (Qt::red);
    m_alertFormat.setUnderlineStyle (QTextCharFormat::SpellCheckUnderline);
}
//------------------------------------------------
/**
 * @brief SyntaxHighlighter::~SyntaxHighlighter
 */
SyntaxHighlighter::~SyntaxHighlighter ()
{
}

//------------------------------------------------------------
/**
 * @brief SyntaxHighlighter::highlightBlock
 * @param text
 */
void SyntaxHighlighter::highlightBlock (const QString &text)
{
    QStringList misspelled (m_spell.misspelledWords (text));

    foreach (const QString &badWord, misspelled) {
        QRegExp expr (("\\b" + badWord + "\\b"));
        int index = text.indexOf (expr, 0);

        while (index >= 0) {
            int length = badWord.length ();
            setFormat (index, length, m_alertFormat);
            index = text.indexOf (expr, index + length);
        }
    }


}
//*******************************************************************************
TextEdit::TextEdit (QWidget * parent) :
    QPlainTextEdit (parent)
{
    document ()->setDocumentMargin (10);

    //initialisation de m_toReplace

    m_toReplace.insert (" .", ".");
    m_toReplace.insert (" ,", ",");
    m_toReplace.insert ("( ", "(");
    m_toReplace.insert (" )", ")");


    createCharMenu ();

    //    connect (document (),
    //             SIGNAL(contentsChange(int,int,int)),
    //             this,
    //             SLOT(onDocContentsChange(int,int,int)));


    m_syntaxHighlighter = new SyntaxHighlighter (document ());
    Config config (0);
    m_syntaxHighlighter->setUnderlineColor (config.optionUnderlineColor ());

}

//}}} --------------------------------------------------------------------------
//{{{ .~TextEdit () -------------------------------------------------------------

TextEdit::~TextEdit ()
{
}

void TextEdit::keyPressEvent(QKeyEvent *e)
{
    QPlainTextEdit::keyPressEvent (e);
    emit documentIsEmpty (document ()->isEmpty ());
}

//}}} --------------------------------------------------------------------------
//{{{ .void .createCharMenu () --------------------------------------------------

void TextEdit::createCharMenu ()
{
    m_insertCharMenu   = new QMenu (tr ("Insérer un caractère..."), this);

    // ** suppression des tentatives avec un grid layout + m_charWidget
    // je souhaitais afficher le sous-menu sous forme de tableau


    // critères de sélection des caractères à afficher

    for (ushort index = 1 ; index <= 0xff ; index++) {
        const QChar car (index);

        // as I never remember operator precedence, I change this

        if (true)
        {
            if (car.isLetterOrNumber ()) continue;
            if (! car.isPrint ()       ) continue;
            if (car.toLatin1 () == car ) continue;
            if (car.isSpace ()         ) continue;

            QWidgetAction * act = new QWidgetAction (m_insertCharMenu);
            act->setText (QString (car));
            m_insertCharMenu->addAction (act);
        }
        else if (! car.isLetterOrNumber ()
                 && car.isPrint ()
                 && (car.toLatin1 () != car)
                 && ! car.isSpace ())
        {
            QWidgetAction * act = new QWidgetAction (m_insertCharMenu);
            act->setText (QString (car));
            m_insertCharMenu->addAction (act);
        }
    }
}

//------------------------------------------------------------
/**
 * @brief TextEdit::contextMenuEvent
 * @param event
 */
void TextEdit::contextMenuEvent (QContextMenuEvent * event)
{
    if (!textCursor ().hasSelection ()) {
        // mettre le curseur à l'endroit du clic

        setTextCursor (cursorForPosition (event->pos ()));
    }

    QTextCursor cursor (textCursor ());

    cursor.select (QTextCursor::WordUnderCursor);

    QString bad (cursor.selectedText ());
    QStringList goodList (m_spell.suggestionsFor (bad));

    ReplaceBy rb;

    QString good;

    switch (rb.run (bad, goodList, &good)) {
    case ReplaceBy::ReplaceHere:
    break;

    case ReplaceBy::ReplaceEveryWhere:
        replaceAll (bad, good);
    break;

    case ReplaceBy::ReplaceAllways:
    break;

    case ReplaceBy::Cancel:
    break;

    }

    return;


    if (!m_spell.isMisspelled (bad)) {
        // menu standard de l'éditeur, amélioré
        QMenu *menu = createStandardContextMenu ();
        menu->addSeparator ();
        menu->addMenu (m_insertCharMenu);

        QAction *action = menu->exec (event->globalPos ());

        if (action) {
            const QString &car (action->text ());
            if (car.length () == 1) {
                insertPlainText (car);
            }
        }

        return;
    }

    QMenu *menu = new QMenu (this);
    if (goodList.isEmpty ()) {  // pas de suggestion
        QAction* tempAction = new QAction ("(Aucune suggestion trouvée)", menu);
        tempAction->setDisabled (true);
        QFont font;
        font.setItalic (true);
        tempAction->setFont (font);
        menu->addAction (tempAction);
    }
    else {
        QMenu* menuSugg = new QMenu (tr ("Remplacer partout par"), menu);

        foreach (const QString& sugg, goodList) {
            QAction* tempAction = new QAction (sugg, menuSugg);
            tempAction->setData (MenuSuggestion);
            QFont font;
            font.setBold (true);
            tempAction->setFont (font);
            menuSugg->addAction (tempAction);
        }
        menu->addMenu (menuSugg);

        QMenu* menuAlwaysReplace = new QMenu (tr ("Toujours remplacer par"), menu);

        foreach (const QString& sugg, goodList) {
            QAction* tempAction = new QAction (sugg, menuSugg);
            tempAction->setData (MenuAlwaysReplace);
            QFont font;
            font.setBold (true);
            tempAction->setFont (font);
            menuAlwaysReplace->addAction (tempAction);
        }
        menu->addMenu (menuAlwaysReplace);
    }
    menu->addSeparator ();

    QAction* tempAction = new QAction (tr ("Ajouter"), menu);
    tempAction->setIcon (QIcon::fromTheme ("accessories-dictionary"));
    tempAction->setData (MenuAddToDic);
    menu->addAction (tempAction);

    tempAction = new QAction (tr ("Ignorer"), menu);
    tempAction->setIcon (QIcon::fromTheme ("media-skip-forward"));
    tempAction->setData (MenuSkip);
    menu->addAction (tempAction);

    tempAction = new QAction (tr ("Supprimer"), menu);
    tempAction->setIcon (QIcon::fromTheme ("edit-delete"));
    tempAction->setData (MenuDelete);
    menu->addAction (tempAction);



    // menu du correcteur

    QAction * action = menu->exec (event->globalPos ());
    if (action) {
        switch (action->data ().toInt ()) {
        case MenuSuggestion: {
            //				int cursorPos = cursor.position ();
            cursor.select (QTextCursor::WordUnderCursor);
            QString bad (cursor.selectedText ());
            QString good (action->text ());
            replaceAll (bad, good);
        }
        break;

        case MenuAlwaysReplace: {
            //				int cursorPos = cursor.position ();
            //				cursor.select (QTextCursor::WordUnderCursor);
            //				QString bad (cursor.selectedText ());
            //				QString good (action->text ());
            //				cursor.select (QTextCursor::Document);
            //				cursor.beginEditBlock ();
            //				cursor.insertText (HSpell::replaced (toPlainText (), bad, good));
            //				cursor.endEditBlock ();
            //				cursor.setPosition (cursorPos);
            //				setTextCursor (cursor);
        }
        break;

        case MenuAddToDic:
            m_spell.add (cursor.selectedText ());
            forceUpdate ();	// force un recalcul
        break;

        case MenuSkip:
            //				m_ortho->addToIgnoreList (misspelledWord);
            forceUpdate ();	// force un recalcul
        break;

        case MenuDelete:
            cursor.deleteChar ();
        break;
        }
    }
    delete menu;
}

//--------------------------------------------------------------------------
/**
 * @brief TextEdit::open
 * @param path
 * @return
 */

bool TextEdit::open (const QString & path)
{
    m_path.clear ();
    if (path.isEmpty()) return false;
    QFile file (path) ;
    if (! file.exists ()) return false;
    QString fileType (Tools::fileType (path));
    qDebug () << "type de fichier :" << fileType;

    if (!fileType.contains ("text")) {
        emit message (tr ("%1 n'est pas reconnu comme fichier texte simple ou HTML"). arg (path));
        QMessageBox::warning	 (this,
                                  tr ("Attention..."),
                                  tr ("Dicodor ne sait ouvrir que des fichiers\nde type texte ou HTML"),
                                  QMessageBox::Ok,
                                  QMessageBox::Ok);
        return false;
    }

    if (!file.open (QIODevice::ReadOnly | QIODevice::Text)) return false;

    QTextStream in (& file) ;
    QString result = in.readAll ();
    if (fileType.contains ("html")) {
        document ()->setHtml (result);
    }
    else {
        document ()->setPlainText (result) ;
    }

    document ()->setModified (false) ;
    setDocumentTitle (path);

    m_path = path;
    emit documentIsEmpty (document ()->isEmpty ());
    return true;
}
//------------------------------------------
/**
 * @brief TextEdit::saveAs
 * @param path
 * @return
 */
bool TextEdit::saveAs (const QString &path)
{
    Config config (0);
    return saveAs (path, config.optionSaveFormat ());

}
//--------------------------------------------------------------------------------------------
/**
 * @brief TextEdit::saveAs
 * @param path
 * @param format
 * @return
 */
bool TextEdit::saveAs (const QString & path, const QByteArray & format)
{
    if (path.isEmpty ()) return false;

    QTextDocument doc;

    QString plainText (document ()->toPlainText ());
    QString html (document ()->toHtml (QByteArray ("utf-8")));

    QString filePath (path);

    if (format == "PDF")
    {
        return false;
    }

    if (format == "plaintext") {
        doc.setPlainText (plainText);
        filePath += ".txt";
    }

    if (format == "ODF") {
        doc.setHtml (html);
        filePath += ".odt";
    }

    if (format == "HTML") {
        doc.setHtml (html);
        filePath += ".html";
    }

    QTextDocumentWriter writer (filePath, format);

    bool ok = writer.write (&doc);

    return ok;

}
//-------------------------------
/**
 * @brief TextEdit::forceUpdate
 */
void TextEdit::forceUpdate ()
{
    setPlainText (toPlainText ());
}
//-----------------------------------------------------------------
/**
 * @brief TextEdit::replaceAll
 * @param bad
 * @param good
 */
void TextEdit::replaceAll (const QString &bad, const QString &good)
{
    //	QTextCursor cursor = textCursor ();
    moveCursor (QTextCursor::Start, QTextCursor::MoveAnchor);
    while (find (bad, QTextDocument::FindWholeWords)) {
        insertPlainText (good);
    }
}
//--------------------------------
/**
 * @brief TextEdit::normalizeText
 */
void TextEdit::normalizeText ()
{
    QString text (toPlainText ());

    QString result;
    const QChar& space = ' ' ;

    foreach (const QChar& car, text) {
//		switch (car.category ()) {
//		case  QChar::Letter_Uppercase:	// 15	Unicode class name Lu
//		case  QChar::Letter_Lowercase:	//16	Unicode class name Ll
//		case  QChar::Letter_Titlecase:	//17	Unicode class name Lt
//		case  QChar::Punctuation_Dash:	//21	Unicode class name Pd
//		case  QChar::Punctuation_Open:	//22	Unicode class name Ps
//		case  QChar::Punctuation_Close:	//23	Unicode class name Pe
//		case  QChar::Punctuation_InitialQuote:	//24	Unicode class name Pi
//		case  QChar::Punctuation_FinalQuote:	//25	Unicode class name Pf
//		case  QChar::Number_DecimalDigit	:	//4	Unicode class name Nd
//		case  QChar::Number_Letter:	 //5	Unicode class name Nl
//		case QChar::Separator_Space:
//		case QChar::Separator_Line:	//8	Unicode class name Zl
//		case QChar::Separator_Paragraph:
//		case QChar::Other_Control:
//		case '\n':

        if  (car.isLetterOrNumber ()
             || car.isPunct ()
             || car == '\''
             || car == '\n') {
            result += car;
        }
        else {
            result += space;
        }
    }

//	QMap <QString, QString>::ConstIterator iter;
//	for (iter = m_toReplace.constBegin () ; iter != m_toReplace.constEnd () ; iter++) {
//		text.replace (iter.key (), iter.value ());
//	}

    setPlainText (result);
}

